// Monad函子是可以变扁（解析出最后的结果）的Pointed函子（具有of方法的函子）
// 一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad

const fs = require('fs')
const fp = require('lodash/fp')

class IO {
  static of (value) {
    return new IO(function() {
      return value
    })
  }

  constructor(fn) {
    this._value = fn
  }

  map(fn) {
    return new IO(fp.flowRight(fn, this._value))
  }

  join() {
    return this._value()
  }

  // 合并map和join, 当返回的值是函子就调用此函数
  flatMap(fn) {
    return this.map(fn).join()
  }
}

// 读取文件是一个不纯的操作，所以使用IO函子封装后使得固定的输入可以得到固定的输出
let readFile = function(filename) {
  return new IO(function() {
    return fs.readFileSync(filename, 'utf-8')
  })
}

let print = function(x) {
  return new IO(function() {
    console.log(x)
    return x
  })
}